#include "imagewindow.hpp"

#include <iostream>

ImageWindow::ImageWindow(string imgpath,MainWindow *main){
    type=IMAGE;

    principalwindow = main;

    //interface load
    Glib::RefPtr<Gnome::Glade::Xml> Widgetstree = Gnome::Glade::Xml::create("interface2.glade");

    Widgetstree->get_widget_derived("imagewindow", imagewindow);
    Widgetstree->get_widget("menuconvert", menuconvert);
    Widgetstree->get_widget("menusave", menusave);
    Widgetstree->get_widget("menuerosion", menuerosion);
    Widgetstree->get_widget("menudilation", menudilation);
    Widgetstree->get_widget("menuopenning", menuopening);
    Widgetstree->get_widget("menuclosing", menuclosing);
    Widgetstree->get_widget("menuinvert", menuinvert);
    Widgetstree->get_widget("menuskel", menuskel);
    Widgetstree->get_widget("menuundo", menuundo);
    Widgetstree->get_widget("menuextremities", menuextremities);
    Widgetstree->get_widget("menuneighbourvalue", menuneighbourvalue);
    Widgetstree->get_widget("menudensity", menudensity);
    Widgetstree->get_widget("menudensity", menudensity);
    Widgetstree->get_widget("togglebutton1x", x1);
    Widgetstree->get_widget("togglebutton4x", x4);
    Widgetstree->get_widget("eventboximage", eventboximage);
    Widgetstree->get_widget("menucontour", menucontour);


    Widgetstree->get_widget("image", image);

    //signal linkage
    menuconvert->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::convertimage));
    menuerosion->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::erosion));
    menudilation->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::dilation));
    menuopening->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::openning));
    menuclosing->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::closing));
    menuinvert->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::invert));
    menuskel->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::skel));
    menusave->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::save));
    menuundo->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::undo));
    menuextremities->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::getextremities));
    menuneighbourvalue->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::neighbourvalue));
    menudensity->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::imagedensity));
    x1->signal_toggled().connect(sigc::mem_fun(*this, &ImageWindow::zoom1x));
    x4->signal_toggled().connect(sigc::mem_fun(*this, &ImageWindow::zoom4x));
    eventboximage->signal_button_press_event().connect(sigc::mem_fun(*this,&ImageWindow::openpopup) );
    menucontour->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::extractcontour));


    imagewindow->signal_delete_event().connect(sigc::mem_fun(*this, &ImageWindow::close));

    //popupmenu

    ActionGroup = Gtk::ActionGroup::create();

    ActionGroup->add(Gtk::Action::create("ContextBifurcation", "Bifurcation"),
          sigc::mem_fun(*this, &ImageWindow::forcetobifurcation));
    ActionGroup->add(Gtk::Action::create("ContextCrossing", "Crossing"),
          sigc::mem_fun(*this, &ImageWindow::forcetocrossing));
    ActionGroup->add(Gtk::Action::create("ContextSuperposition", "Superposition"),
          sigc::mem_fun(*this, &ImageWindow::forcetosuperposition));
    ActionGroup->add(Gtk::Action::create("ContextNone", "NONE"),
          sigc::mem_fun(*this, &ImageWindow::forcenone));


    UIManager = Gtk::UIManager::create();
    UIManager->insert_action_group(ActionGroup);

    Glib::ustring ui_info =
        "<ui>"
        "  <popup name='PopupMenu'>"
        "    <menuitem action='ContextBifurcation'/>"
        "    <menuitem action='ContextCrossing'/>"
        "    <menuitem action='ContextSuperposition'/>"
        "    <menuitem action='ContextNone'/>"
        "  </popup>"
        "</ui>";

    UIManager->add_ui_from_string(ui_info);

    MenuPopup = dynamic_cast<Gtk::Menu*>(
    UIManager->get_widget("/PopupMenu"));


    //Initializations
    imagepath = imgpath;
    interfaceimage = new Glib::RefPtr<Gdk::Pixbuf>;
    *interfaceimage = Gdk::Pixbuf::create_from_file(imagepath);
    workimage=pixbuftoColor(interfaceimage);
    image->set(*interfaceimage);

    interfaceimage4x=NULL;
    undoimage=NULL;
    crossmap=NULL;

    zoom=false;

    id=principalwindow->emptyid;
    principalwindow->emptyid++;

    string title("ID: ");
    std::stringstream ss;
    ss << id;
    title+=ss.str();
    imagewindow->set_title(title);
    imagewindow->show_all();
}


ImageWindow::ImageWindow(Image* img,MainWindow *main){
    type=IMAGE;

    principalwindow = main;

    //interface load
    Glib::RefPtr<Gnome::Glade::Xml> Widgetstree = Gnome::Glade::Xml::create("interface2.glade");

    Widgetstree->get_widget_derived("imagewindow", imagewindow);
    Widgetstree->get_widget("menusave", menusave);
    Widgetstree->get_widget("menuconvert", menuconvert);
    Widgetstree->get_widget("menuerosion", menuerosion);
    Widgetstree->get_widget("menudilation", menudilation);
    Widgetstree->get_widget("menuopenning", menuopening);
    Widgetstree->get_widget("menuclosing", menuclosing);
    Widgetstree->get_widget("menuinvert", menuinvert);
    Widgetstree->get_widget("menuskel", menuskel);
    Widgetstree->get_widget("menuundo", menuundo);
    Widgetstree->get_widget("menuextremities", menuextremities);
    Widgetstree->get_widget("menuneighbourvalue", menuneighbourvalue);
    Widgetstree->get_widget("menudensity", menudensity);
    Widgetstree->get_widget("menucontour", menucontour);
    Widgetstree->get_widget("togglebutton1x", x1);
    Widgetstree->get_widget("togglebutton4x", x4);
    Widgetstree->get_widget("eventboximage", eventboximage);


    Widgetstree->get_widget("image", image);

    menuconvert->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::convertimage));
    menuerosion->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::erosion));
    menudilation->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::dilation));
    menuopening->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::openning));
    menuclosing->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::closing));
    menuinvert->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::invert));
    menuskel->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::skel));
    menusave->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::save));
    menuundo->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::undo));
    menuextremities->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::getextremities));
    menuneighbourvalue->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::neighbourvalue));
    menudensity->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::imagedensity));
    x1->signal_toggled().connect(sigc::mem_fun(*this, &ImageWindow::zoom1x));
    x4->signal_toggled().connect(sigc::mem_fun(*this, &ImageWindow::zoom4x));
    eventboximage->signal_button_press_event().connect(sigc::mem_fun(*this,&ImageWindow::openpopup) );
    menucontour->signal_activate().connect(sigc::mem_fun(*this, &ImageWindow::extractcontour));



    imagewindow->signal_delete_event().connect(sigc::mem_fun(*this, &ImageWindow::close));

    //popupmenu

    ActionGroup = Gtk::ActionGroup::create();

    ActionGroup->add(Gtk::Action::create("ContextBifurcation", "Bifurcation"),
          sigc::mem_fun(*this, &ImageWindow::forcetobifurcation));
    ActionGroup->add(Gtk::Action::create("ContextCrossing", "Crossing"),
          sigc::mem_fun(*this, &ImageWindow::forcetocrossing));
    ActionGroup->add(Gtk::Action::create("ContextSuperposition", "Superposition"),
          sigc::mem_fun(*this, &ImageWindow::forcetosuperposition));
    ActionGroup->add(Gtk::Action::create("ContextNone", "NONE"),
          sigc::mem_fun(*this, &ImageWindow::forcenone));


    UIManager = Gtk::UIManager::create();
    UIManager->insert_action_group(ActionGroup);

    Glib::ustring ui_info =
        "<ui>"
        "  <popup name='PopupMenu'>"
        "    <menuitem action='ContextBifurcation'/>"
        "    <menuitem action='ContextCrossing'/>"
        "    <menuitem action='ContextSuperposition'/>"
        "    <menuitem action='ContextNone'/>"
        "  </popup>"
        "</ui>";

    UIManager->add_ui_from_string(ui_info);

    MenuPopup = dynamic_cast<Gtk::Menu*>(
    UIManager->get_widget("/PopupMenu"));


    //Initializations
    workimage=img;
    interfaceimage=imagetoPixbuf(workimage);
    image->set(*interfaceimage);

    undoimage=NULL;
    crossmap=NULL;
    interfaceimage4x=NULL;
    zoom=false;

    id=principalwindow->emptyid;
    principalwindow->emptyid++;

    string title("ID: ");
    std::stringstream ss;
    ss << id;
    title+=ss.str();
    imagewindow->set_title(title);
    imagewindow->show_all();
}

ImageWindow::~ImageWindow(){

    imagewindow->destroy_();
    delete interfaceimage;
    delete interfaceimage4x;
    delete imagewindow;
    delete workimage;
    delete undoimage;

}

bool ImageWindow::close(GdkEventAny *){
    principalwindow->closethisObject(this);
    return true;
}

void ImageWindow::outImage(Image* img){
    if(img!=NULL){
        if(principalwindow->newwindow()){
            principalwindow->objectsopen.push_back(new ImageWindow(img,principalwindow));
            principalwindow->updateimagetree();
        }
        else{
            if(undoimage!=NULL)
                delete undoimage;
            undoimage=workimage;
            workimage=img;
            image->clear();
            (*interfaceimage).reset();
            delete interfaceimage;
            interfaceimage=imagetoPixbuf(workimage);
            image->set(*interfaceimage);
            principalwindow->updateimagetree();
        }
    }
    else{
        cout << "Error while perform image atualization" << std::endl;
        exit(14);
    }
}

void ImageWindow::convertimage(){
    Image *temp=NULL;

    if(principalwindow->conversionpallet==NULL){
        principalwindow->openConvPallet();
    }
    switch(workimage->getType()){
        case Image::BINARY:
            if(principalwindow->conversionpallet->binaryto==1)
                temp=workimage->convert(Image::GREY,principalwindow->conversionpallet->binarytogreyoption);
            else
                temp=workimage->convert(Image::COLOR,principalwindow->conversionpallet->binarytocoloroption);
            break;
        case Image::GREY:
            if(principalwindow->conversionpallet->greyto==1)
                temp=workimage->convert(Image::BINARY,principalwindow->conversionpallet->greytobinaryoption,principalwindow->conversionpallet->setedvalue);
            else
                temp=workimage->convert(Image::COLOR,principalwindow->conversionpallet->greytocoloroption);
            break;
        case Image::COLOR:
            if(principalwindow->conversionpallet->colorto==1)
                temp=workimage->convert(Image::BINARY,principalwindow->conversionpallet->colortobinaryoption);
            else
                temp=workimage->convert(Image::GREY,principalwindow->conversionpallet->colortograyoption);
            break;
        default:
            break;
    }

    outImage(temp);

}

void ImageWindow::erosion(){
    Image *temp;

    if(principalwindow->morphpallet==NULL){
        principalwindow->openMorphPallet();
    }

    if(principalwindow->morphpallet->se_erosion==NULL)
        principalwindow->morphpallet->createStructElem();

    if(workimage->getType()==Image::GREY){
        temp=MOperator::erosion((Grey_Image*)workimage,NULL,principalwindow->morphpallet->se_erosion);
        if(principalwindow->morphpallet->iterations[0]>1)
            for(int i=1;i<principalwindow->morphpallet->iterations[0];i++)
                MOperator::erosion((Grey_Image*)temp,(Grey_Image*)temp,principalwindow->morphpallet->se_erosion);
    }
    else if(workimage->getType()==Image::BINARY){
        temp=MOperator::erosion((Binary_Image*)workimage,NULL,principalwindow->morphpallet->se_erosion);
        if(principalwindow->morphpallet->iterations[0]>1)
            for(int i=1;i<principalwindow->morphpallet->iterations[0];i++)
                MOperator::erosion((Binary_Image*)temp,(Binary_Image*)temp,principalwindow->morphpallet->se_erosion);
    }
    else{
        invalidtyperror();
        return;
    }
    outImage(temp);


}


void ImageWindow::invert(){

    Image* temp;
    temp=workimage->copy();
    temp->invert();

    outImage(temp);

}

void ImageWindow::skel(){
    Image *temp;

    if(workimage->getType()==Image::GREY)
        temp=MOperator::skel((Grey_Image*)workimage);
    else if(workimage->getType()==Image::BINARY)
        temp=MOperator::skel((Binary_Image*)workimage);
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);

}

void ImageWindow::getcontour(){
    ContourExtractor extractor;
    Contour* contorno = extractor.getContour((Binary_Image*)workimage,EIGHTNEIGHBOURHOOD);
    if(workimage->getType()==Image::BINARY){
        principalwindow->objectsopen.push_back(new ContourWindow(contorno,principalwindow));
        principalwindow->updateimagetree();
    }
    else{
        invalidtyperror();
        return;
    }

}


void ImageWindow::dilation(){
    Image *temp;

    if(principalwindow->morphpallet==NULL){
        principalwindow->openMorphPallet();
    }

    if(principalwindow->morphpallet->se_dilation==NULL)
        principalwindow->morphpallet->createStructElem();

    if(workimage->getType()==Image::GREY){
        temp=MOperator::dilation((Grey_Image*)workimage,NULL,principalwindow->morphpallet->se_dilation);
        if(principalwindow->morphpallet->iterations[1]>1)
            for(int i=1;i<principalwindow->morphpallet->iterations[1];i++)
                MOperator::dilation((Grey_Image*)temp,(Grey_Image*)temp,principalwindow->morphpallet->se_dilation);
    }
    else if(workimage->getType()==Image::BINARY){
        temp=MOperator::dilation((Binary_Image*)workimage,NULL,principalwindow->morphpallet->se_dilation);
        if(principalwindow->morphpallet->iterations[1]>1)
            for(int i=1;i<principalwindow->morphpallet->iterations[1];i++)
                MOperator::dilation((Binary_Image*)temp,(Binary_Image*)temp,principalwindow->morphpallet->se_dilation);
    }
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);


}

void ImageWindow::save(){
    Gtk::FileChooserDialog dialog("Please choose a file",
          Gtk::FILE_CHOOSER_ACTION_SAVE);
    dialog.set_transient_for(*(this->imagewindow));

    //Add response buttons the the dialog:
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
    dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);

      //Show the dialog and wait for a user response:
    int result = dialog.run();

    //Handle the response:
    switch(result)
    {
        case(Gtk::RESPONSE_OK):
        {
            (*interfaceimage)->save(dialog.get_filename(),"png");
            break;
        }
        case(Gtk::RESPONSE_CANCEL):
        {
            break;
        }
        default:
        {
            break;
        }
  }
}

void ImageWindow::undo(){
    std::cout << "undo!" << std::endl;
    if(undoimage!=NULL){
        delete interfaceimage;
        workimage=undoimage;
        interfaceimage=imagetoPixbuf(workimage);
        image->set(*interfaceimage);
        undoimage=NULL;
    }
}



string ImageWindow::getImageType(){
    switch(workimage->getType()){
        case Image::BINARY:
            return string("Binary");
        case Image::GREY:
            return string("Grey");
        case Image::COLOR:
            return string("Color");
        default:
            return string("invalid");
    }
}

void ImageWindow::neighbourvalue(){
    Image *temp;

    if(workimage->getType()==Image::BINARY)
        temp=MOperator::neighbourvalue((Binary_Image*)workimage);
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);
}

void ImageWindow::getextremities(){
    Image *temp;

    if(workimage->getType()==Image::BINARY)
        temp=findExtremitiesFromSkel((Binary_Image*)workimage,NULL);
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);
}

void ImageWindow::imagedensity(){
    Image *temp;

    if(principalwindow->analysispallet==NULL)
        principalwindow->openAnalysisPallet();

    if(workimage->getType()==Image::BINARY)
        temp=density((Binary_Image*)workimage,NULL,principalwindow->analysispallet->samplesizex,principalwindow->analysispallet->samplesizey);
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);
}

void ImageWindow::testfunction(){

    if(workimage->getType()==Image::GREY){
        ContourExtractorBranch extractor;
        principalwindow->objectsopen.push_back(new ContourWindow(extractor.getContour((Grey_Image*)workimage,crossmap),principalwindow));
        principalwindow->updateimagetree();
    }
    else{
        invalidtyperror();
        return;
    }

}


void ImageWindow::closing(){
    Image *temp;

    if(principalwindow->morphpallet==NULL){
        principalwindow->openMorphPallet();
    }

    if(principalwindow->morphpallet->se_erosion==NULL)
        principalwindow->morphpallet->createStructElem();

    if(workimage->getType()==Image::GREY){
        temp=MOperator::dilation((Grey_Image*)workimage,NULL,principalwindow->morphpallet->se_close);
        MOperator::erosion((Grey_Image*)temp,(Grey_Image*)temp,principalwindow->morphpallet->se_close);
    }
    else if(workimage->getType()==Image::BINARY){
        temp=MOperator::dilation((Binary_Image*)workimage,NULL,principalwindow->morphpallet->se_close);
        MOperator::erosion((Binary_Image*)temp,(Binary_Image*)temp,principalwindow->morphpallet->se_close);
    }
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);
}

void ImageWindow::openning(){
    Image *temp;

    if(principalwindow->morphpallet==NULL){
        principalwindow->openMorphPallet();
    }

    if(principalwindow->morphpallet->se_erosion==NULL)
        principalwindow->morphpallet->createStructElem();

    if(workimage->getType()==Image::GREY){
        temp=MOperator::erosion((Grey_Image*)workimage,NULL,principalwindow->morphpallet->se_open);
        MOperator::dilation((Grey_Image*)temp,(Grey_Image*)temp,principalwindow->morphpallet->se_open);
    }
    else if(workimage->getType()==Image::BINARY){
        temp=MOperator::erosion((Binary_Image*)workimage,NULL,principalwindow->morphpallet->se_open);
        MOperator::dilation((Binary_Image*)temp,(Binary_Image*)temp,principalwindow->morphpallet->se_open);
    }
    else{
        invalidtyperror();
        return;
    }

    outImage(temp);
}

void ImageWindow::invalidtyperror(){

    Gtk::MessageDialog message("Invalid type! This operation don't act in current image type",false,Gtk::MESSAGE_ERROR,Gtk::BUTTONS_CLOSE,true);
    message.run();
}

void ImageWindow::extractcontour(){

    if(workimage->getType()==Image::BINARY){
        principalwindow->objectsopen.push_back(new ContourWindow(Contour::newFromImage((Binary_Image*)workimage),principalwindow));
        principalwindow->updateimagetree();
    }
    else{
        invalidtyperror();
        return;
    }
}

Image* ImageWindow::getImage(){
    return workimage;
}



void ImageWindow::zoom1x(){
    x4->set_active(false);
    image->clear();
    image->set(*interfaceimage);
    zoom=false;

}
void ImageWindow::zoom4x(){
    x1->set_active(false);
    if(interfaceimage4x==NULL){
        interfaceimage4x = new Glib::RefPtr<Gdk::Pixbuf>;
        *interfaceimage4x = (*interfaceimage)->scale_simple(4*workimage->getWidth(),4*workimage->getHeight(),Gdk::INTERP_NEAREST);
    }
    image->clear();
    image->set(*interfaceimage4x);
    zoom=true;
}

bool ImageWindow::hasZoom(){
    return zoom;
}


void ImageWindow::forcetobifurcation(){
    if(zoom)
        principalwindow->forcercclassification(Position(mousex/4,mousey/4),CriticalRegion::BIFURCATION);
    else
        principalwindow->forcercclassification(Position(mousex,mousey),CriticalRegion::BIFURCATION);
}
void ImageWindow::forcetocrossing(){
    if(zoom)
        principalwindow->forcercclassification(Position(mousex/4,mousey/4),CriticalRegion::CROSSING);
    else
        principalwindow->forcercclassification(Position(mousex,mousey),CriticalRegion::CROSSING);
}

void ImageWindow::forcetosuperposition(){
    if(zoom)
        principalwindow->forcercclassification(Position(mousex/4,mousey/4),CriticalRegion::SUPERPOSITION);
    else
        principalwindow->forcercclassification(Position(mousex,mousey),CriticalRegion::SUPERPOSITION);
}

void ImageWindow::forcenone(){
    if(zoom)
        principalwindow->forcercclassification(Position(mousex/4,mousey/4),CriticalRegion::NONE);
    else
        principalwindow->forcercclassification(Position(mousex,mousey),CriticalRegion::NONE);
}

bool ImageWindow::openpopup(GdkEventButton* event){
    if( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) )
  {
    if(MenuPopup)
      MenuPopup->popup(event->button, event->time);
      mousex=event->x;
      mousey=event->y;

    return true; //It has been handled.
  }
  else
    return false;

}
